package com.gw.youTubeAudioAPI.decode.bitstream
{
	//icy libs
	import com.codeazur.as3icy.data.MPEGFrame;
	import com.codeazur.as3icy.decoder.Decoder;
	import com.codeazur.as3icy.decoder.OutputBuffer;
	
	import flash.events.EventDispatcher;
	import flash.utils.ByteArray;
	
	/**
	 * processes an mp3 bitstream into raw samples 
	 * code adapted from as3Icy.ICYSound.as (in turn ported from jLayer)
	 * http://github.com/claus/as3icy
	 **/
	
	public class MP3toRaw extends EventDispatcher
	{
		private static const SAMPLE_DATA_SIZE:uint = 8192;
		private static const SAMPLE_DATA_BYTES:uint = (SAMPLE_DATA_SIZE << 3);
		
		private var bitStream:		ByteArray = new ByteArray();
		private var sampleBuffer:	ByteArray = null;
		private var frameData:		ByteArray = new ByteArray();
		
		private var headerIndex:uint = 0;
		private var crcIndex:uint = 0;
		
		//icy decoder components
		private var _mpegFrame:MPEGFrame;
		private var decoder:Decoder;
		
		//icy read function references / process states
		private var readFunc:Function = readIdle;//current process
		private var readFuncContinue:Function;//next process
		
		public function MP3toRaw(sampleRate:int = 44100, channels:int = 2)
		{
			//init icy components
			decoder 	 = new Decoder(new OutputBuffer(sampleRate, channels)); //set appropriate samplerate for buffer - gw
			_mpegFrame 	 = new MPEGFrame();
		}
		//-decoder functionality (lifted from icy)----------------------------
		public function decodeBitStream(bitStream:ByteArray, sampleBuffer:ByteArray):void
		{
			//init
			//sampleBuffer.clear();//reuse
			this.sampleBuffer = sampleBuffer;
			this.bitStream = bitStream;
			readFunc = readFrameHeader;
			
			//process - should this really be a tight loop?
			while (readFunc());
		}
		//-read functions/processes------------------------------------------
		private function readIdle():Boolean { return false; }
		private function readFrameHeader():Boolean 
		{
			//trace("reading header------");
			var ret:Boolean = true;
			
			if(bitStream.bytesAvailable >= 1) 
			{
				try {//header is 32bits AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
					_mpegFrame.setHeaderByteAt(headerIndex, bitStream.readUnsignedByte()); //advance one byte
					if (++headerIndex == 4) {//check crc
						readFunc = _mpegFrame.hasCRC ? readCRC : readFrame;
						headerIndex = 0;
					}
				}
				catch (e:Error) {
					//trace("MP3toRaw::readFrameHeader: "+e.message);
					headerIndex = 0; //try and catch the header again...
				}
			} 
			else 
			{
				ret = false;
			}
			return ret;
		}
		private function readCRC():Boolean 
		{
			var ret:Boolean = true;
			if (bitStream.bytesAvailable >= 1) {
				try {
					_mpegFrame.setCRCByteAt(crcIndex, bitStream.readUnsignedByte());
					if (++crcIndex == 2) {
						readFunc = readFrame;
						crcIndex = 0;
					}
				}
				catch (e:Error) {
					trace(e.message);
					readFunc = readFrameHeader;
					crcIndex = 0;
				}
			} else {
				ret = false;
			}
			return ret;
		}
		private function readFrame():Boolean 
		{
			//trace("decoding frame:",_mpegFrame.toString());
			var ret:Boolean = true;
			var len:uint = _mpegFrame.size;
			
			if (bitStream.bytesAvailable < len) {
				trace("less bitstream available than required by mp3 frame");
				return false;
			} 
			
			//this whole bit is screwy vvvv
			frameData.clear();//reuse
			bitStream.readBytes(frameData, 0, len); //bitstream to frameData
			_mpegFrame.data.writeBytes(frameData); 	//framedata to _mpegFrame.data
			_mpegFrame.data.position = 0;			
			//------------------------------
			
			//decode the mpeg frame we just read
			decoder.decodeFrame(_mpegFrame);
			
			var ob:Vector.<Number> = decoder.outputBuffer.getBuffer();//32 samples of 16bit signed PCM
			var bufferUsed:int = decoder.outputBuffer.getBufferLength();
			if ( !bufferUsed ) return false; //don't read discarded buffers
			for (var i:int = 0; i < bufferUsed; i++) //n channel interleaved
			{
				//need to convert:
				//16bit signed pcm to 32bit float
				//samplerate to 44,100
				/*protected AudioFormat getAudioFormat()
				{
					if (fmt==null)
					{
						Decoder decoder = getDecoder();
						fmt = new AudioFormat(	decoder.getOutputFrequency(),
												16, 
												decoder.getOutputChannels(), true, false);
					}
					return fmt;
				}
				*/
				sampleBuffer.writeFloat(ob[i]);
			}
			decoder.outputBuffer.clear_buffer();
				
			//dispatchEvent(new ICYFrameEvent(ICYFrameEvent.FRAME, _mpegFrame, false, true));
			
			//read next header
			_mpegFrame = new MPEGFrame();
			readFunc = readFrameHeader;
			return true;
		}//end read frame
	}
}
